<script setup lang="ts">
import AMapLoader from "@amap/amap-jsapi-loader";
import breathRedPng from "./assets/breath_red.png";
import { onMounted, ref,Ref } from "vue";

// 地图实例
const mapInstance:Ref<any> = ref(null);
const locaInstance:Ref<any> = ref(null);

const props = defineProps({
  // 地图样式
  mapStyle: { type:String, default:"amap://styles/darkblue" },
  // 高德地图配置
  apiConfig:{ type: Object, default: () => ({ 
    key:"你自己在高德地图平台申请的key",
    version:"2.0",
    plugins:["AMap.DistrictSearch","AMap.ControlBar"]
  }) },
  // 区域名称
  areaName:{ type:String, default:"西安市" },
  // 显示下级行政区级数（行政区级别包括：国家、省/直辖市、市、区/县4个级别），商圈为区/县下一 级
  subdistrict:{ type:Number, default: 0 },
  // 是否返回行政区边界坐标点，默认值：base，不返回行政区边界坐标点，取值：all，返回完整行政区边界坐标点
  extensions:{ type:String,default:"all" },
  // 关键字对应的行政区级别或商圈，可选值： country：国家 province：省/直辖市 city：市 district：区/县 biz_area：商圈
  level:{ type:String, default:"city" },
  // 边界配置
  polylineConfig:{ type:Object, default:() => ({
    // 是否显示边界线
    show:true,
    // 是否显示边界线以外的区域
    showOuter:false,
    // 边界线条颜色
    strokeColor:"#0099ff",
    // 边界线条粗细
    strokeWidth:4
  }) },
  // 是否支持双击鼠标放大
  doubleClickZoom:{ type:Boolean, default:true },
  // 支持设置中心点
  center:{ type:Array, default:() => [108.965394,34.279331] },
  // 地图缩放等级
  zoom:{ type:Number, default:10 },
  // 地图缩放范围
  zooms:{ type:Array, default:() => [8,18] },
  // 是否展示卫星地图
  isShowSatellite:{ type:Boolean, default: false },
  // 是否展示卫星路网
  isShowRoadNet:{ type:Boolean, default: false },
  // 是否3D显示
  isShow3D:{ type:Boolean, default: false },
  // 俯视角度
  pitch:{ type:Number, default: 40 },
  // 地图方位控制器的配置
  controllBarConfig:{ type:Object, default:() => ({
    // 是否显示方位控制器
    show:true,
    // 是否显示缩放按钮
    showZoomBar:true,
    // 是否显示倾斜、旋转按钮
    showControllButton:true,
    // 距离顶部的距离
    positionTop:10,
    // 距离右侧的距离
    positionRight:10
  }) },
  // 3D墙体的配置
  object3dWallConfig:{ type:Object, default:() => ({
    // 是否显示3D墙体
    show:true,
    // 层级
    zIndex:1,
    // 墙高
    wallHeight: -8000,
    // 墙体颜色
    color:"#0099ff",
    // 是否进行颜色混合
    transparent:true,
    // 控制显示正反面，both front back
    backOrFront:"both"
  }) },
  // 是否绘制线条
  isDrawPolyline:{ type:Boolean, default:false },
  // 线条数据
  polylineData:{ type:Array, default:() => [
    {
      path:[
        [108.764184,34.166458],
        [108.994896,34.186909],
        [108.964684,34.29476],
      ],
      // 是否显示描边
      isOutline:true,
      // 描边颜色
      outlineColor:"#ffeeff",
      // 描边宽度
      borderWeight:3,
      // 线条颜色
      strokeColor:"#0099ff",
      // 线条透明度
      strokeOpacity:1,
      // 线条的宽度
      strokeWeight:6,
      // 线条的样式
      strokeStyle:"solid",
      // 勾勒形状轮廓的虚线和间隙的样式
      strokeDasharray:[0,0,0],
      // 折线拐点的绘制样式
      lineJoin:"round",
      // 折线两端线帽的绘制样式
      lineCap:"round",
      // 折线覆盖物的叠加顺序
      zIndex:50
    },
    {
      path:[
        [108.86297,34.341802],
        [109.002359,34.346904],
        [108.932321,34.268066],
      ],
      // 是否显示描边
      isOutline:true,
      // 描边颜色
      outlineColor:"#ffeeff",
      // 描边宽度
      borderWeight:3,
      // 线条颜色
      strokeColor:"#ff0000",
      // 线条透明度
      strokeOpacity:1,
      // 线条的宽度
      strokeWeight:6,
      // 线条的样式
      strokeStyle:"solid",
      // 勾勒形状轮廓的虚线和间隙的样式
      strokeDasharray:[0,0,0],
      // 折线拐点的绘制样式
      lineJoin:"round",
      // 折线两端线帽的绘制样式
      lineCap:"round",
      // 折线覆盖物的叠加顺序
      zIndex:50
    }
  ] },
  // 是否绘制点位
  isDrawPoint:{ type:Boolean, default:true },
  // 点位的数据
  pointData:{ type:Array, default:() => [
    {
      iden:"点位1",
      lnglat:[108.764184,34.166458],
      offset:[-13,-30],
      icon:{
        size:[40, 50],
        image:'//vdata.amap.com/icons/b18/1/2.png',
        imageSize:[0, -60],
        imageOffset:[40, 50],
      }
    },
    {
      iden:"点位2",
      lnglat:[108.994896,34.186909],
      offset:[-13,-30],
      icon:{
        size:[40, 50],
        image:'//vdata.amap.com/icons/b18/1/2.png',
        imageSize:[0, -60],
        imageOffset:[40, 50],
      }
    },
    {
      iden:"点位3",
      lnglat:[108.964684,34.29476],
      offset:[-13,-30],
      icon:{
        size:[40, 50],
        image:'//vdata.amap.com/icons/b18/1/2.png',
        imageSize:[0, -60],
        imageOffset:[40, 50],
      }
    }
  ] },
  // LOCA配置
  locaConfig:{ type:Object, default:() => ({
    // 是否展示
    show:true,
    // 资源类型 url data
    sourceType:"data",
    // 脉冲线的版本
    version:"2.0"
  }) },
  // 脉冲线数据
  locaData:{ type:Object, default:() => ({
    // 脉冲线数据的url
    geoJsonUrl:"",
    // 脉冲线的data
    geoJsonData:{
      type:"FeatureCollection",
      features:[
        {
          type:"Feature",
          id:Date.now(),
          properties:{},
          geometry:{
            type:"LineString",
            coordinates:[
              [108.764184,34.166458],
              [108.994896,34.186909],
              [108.964684,34.29476],
            ]
          },
          bbox:[108.764184,34.166458,108.964684,34.29476]
        },
        {
          type:"Feature",
          id:Date.now(),
          properties:{},
          geometry:{
            type:"LineString",
            coordinates:[
              [108.86297,34.341802],
              [109.002359,34.346904],
              [108.932321,34.268066],
            ]
          },
          bbox:[108.86297,34.341802,108.932321,34.268066]
        }
      ]
    },
    // 脉冲线的图层样式
    globalStyle:{
      zIndex:10,
      opacity:1,
      visible:true,
      zooms:[2,22],
    },
    // 脉冲线的样式
    layerStyle:{
      altitude:0,
      lineWidth:10,
      headColor:"#00ff99",
      trailColor:"#0099ff",
      interval:0.75,
      duration:2000,
    }
  }) },
  // 是否显示预警点位
  isShowPointWarning:{ type:Boolean,default:true },
  // 预警点位的集合
  warningList:{ type:Array,default:() => [[108.994896,34.186909], [109.002359,34.346904]] },
  // 预警点位的图层配置，暂时没写，放在代码中写死，如果需要拓展的话，在这里添加就行
});

// 渲染边界线
const renderPolyLine = (bounds = []) => {
  const { polylineConfig } = props;
  if(polylineConfig.show){
    for(let i = 0;i <bounds.length; i++){
      new AMap.Polyline({
        path:bounds[i],
        strokeColor:polylineConfig.strokeColor,
        strokeWidth:polylineConfig.strokeWidth,
        map:mapInstance.value
      })
    }
  }
}

// 渲染3D墙体
const render3dWall = (bounds = []) => {
  const { object3dWallConfig, apiConfig } = props;

  if(apiConfig.version === "1.4.15"){
    if(object3dWallConfig.show && !!mapInstance.value){
      // 定义一个3D图层
      const object3DLayer = new AMap.Object3DLayer({
        zIndex:object3dWallConfig.zIndex
      });

      // 创建墙体
      const wall = new AMap.Object3D.Wall({
        path:bounds,
        height:object3dWallConfig.wallHeight,
        color:object3dWallConfig.color,
      })

      wall.transparent = object3dWallConfig.transparent;
      wall.backOrFront = object3dWallConfig.backOrFront;
      object3DLayer.add(wall);
      mapInstance.value.add(object3DLayer);
    }
  }else if(apiConfig.version === "2.0"){
    for(let i = 0;i<bounds.length; i++){
      new AMap.Polyline({
        path:bounds[i],
        strokeColor:object3dWallConfig.color,
        strokeWidth:object3dWallConfig.height,
        map:mapInstance.value
      })
    }
  }
}

// 渲染方位控制器
const renderControlBar = () => {
  const { controllBarConfig } = props;
  if(controllBarConfig.show){
    mapInstance.value.addControl(
      new AMap.ControlBar({
        showZoomBar:controllBarConfig.showZoomBar,
        showControlButton:controllBarConfig.showControlButton,
        position:{
          right:`${controllBarConfig.positionRight}px`,
          top:`${controllBarConfig.positionTop}px`
        }
      })
    )
  }
}

// 渲染线条
const renderLine = () => {
  const { polylineData }:any = props;
  if(props.isDrawPolyline){
    const polyLines = [];
    for(let i = 0;i < polylineData.length; i++){
      const polyline = new AMap.Polyline({
        path:polylineData[i].path,
        // 是否显示描边
        isOutline:polylineData[i].isOutline,
        // 描边颜色
        outlineColor:polylineData[i].outlineColor,
        // 描边宽度
        borderWeight:polylineData[i].borderWeight,
        // 线条颜色
        strokeColor:polylineData[i].strokeColor,
        // 线条透明度
        strokeOpacity:polylineData[i].strokeOpacity,
        // 线条的宽度
        strokeWeight:polylineData[i].strokeWeight,
        // 线条的样式
        strokeStyle:polylineData[i].strokeStyle,
        // 勾勒形状轮廓的虚线和间隙的样式
        strokeDasharray:polylineData[i].strokeDasharray,
        // 折线拐点的绘制样式
        lineJoin:polylineData[i].lineJoin,
        // 折线两端线帽的绘制样式
        lineCap:polylineData[i].lineCap,
        // 折线覆盖物的叠加顺序
        zIndex:polylineData[i].zIndex
      })
      polyLines.push(polyline);
    }
    mapInstance.value.add(polyLines);
  }
}

// 渲染点位
const renderPoint = () => {
  const { isDrawPoint,pointData }:any = props;
  if(isDrawPoint){
    // 渲染少量点位使用Marker对象，点位过多会存在页面卡顿 - START
    // const markers = [];
    // for(let i = 0;i< pointData.length;i++){
    //   // 定义图标
    //   let icon = pointData[i].icon;
    //   if(typeof pointData[i].icon !== "string"){
    //     icon = new AMap.Icon({
    //       size:new AMap.Size(...pointData[i].icon.size),
    //       image:pointData[i].icon.image,
    //       imageSize:pointData[i].icon.imageSize,
    //       imageOffset:pointData[i].icon.iimageOffsetmage,
    //     })
    //   }
    //   const maker = new AMap.Marker({
    //     position:new AMap.LngLat(...pointData[i].lnglat),
    //     offset:new AMap.Pixel(...pointData[i].offset),
    //     icon
    //   })
    //   maker.on("click",function(){
    //     alert(pointData[i].iden);
    //   })
    //   markers.push(maker);
    // }
    // mapInstance.value.add(markers);
    // 渲染少量点位使用Marker对象，点位过多会存在页面卡顿 - END

    // 渲染批量点位使用MassMarks对象，相当于添加新的图层 - START
    // styles存储所有的图标样式
    const styles:Array<any> = [];
    // 为了兼容原来的方式，构造一个新对象存储在styles
    // anchor为图标在地图上的偏移量，url等同于Marker对象的Icon的image
    pointData.forEach((point:any) => !(styles.find(icon => icon.image == point.icon.image)) && styles.push({...point.icon, anchor:new AMap.Pixel(15,35), url:point.icon.image}));
    // 构造新的点位集合，因为MassMarks对象的点位中必须有lnglat经纬度
    // 与Marker对象的经纬度区别在于Lat的L要小写
    // style为当前点位的图标样式在styles中的索引
    const points = pointData.map( (point:any) => ({ ...point,lnglat: point.lngLat, style:styles.findIndex(icon => icon.url === point.icon.image) }) );
    // 生成一个海量点图层，第一个参数为点位数据，第二个参数为一个options，选项样式
    const massMarks = new AMap.MassMarks(points,{
      zIndex:114,
      zooms:[3,19],
      style:styles
    })
    // 将当前海量点图层添加到地图上
    massMarks.setMap(mapInstance.value);
    // 点位添加点击事件
    massMarks.on("click", function ({ data }) {
      alert(data)
    });
    // 渲染批量点位使用MassMarks对象，相当于添加新的图层 - END
  }
}

// 渲染脉冲线
const renderLoca = () => {
  const { locaConfig, locaData } = props;

  if(!locaConfig.show) return;

  locaInstance.value = new Loca.Container({
    map:mapInstance.value
  })

  // 获取geoJson数据
  const sourceParams:{ [key:string]:any } = {};
  const sourceTypeToDataKey:{ [key:string]:any } = {
    url:"geoJsonUrl",
    data:"geoJsonData"
  }
  sourceParams[locaConfig.sourceType] = locaData[sourceTypeToDataKey[locaConfig.sourceType]];
  // sourceParams = { data: locaData.geoJsonData }

  const geo = new Loca.GeoJSONSource(sourceParams);
  // 添加脉冲线图层
  const layer = new Loca.PulseLineLayer({
    loca:locaInstance.value,
    zIndex:locaData.globalStyle.zIndex,
    opacity:locaData.globalStyle.opacity,
    visible:locaData.globalStyle.visible,
    zooms:locaData.globalStyle.zooms,
  })

  layer.setSource(geo);

  // 设置脉冲线样式
  layer.setStyle(locaData.layerStyle)

  locaInstance.value.add(layer);

  locaInstance.value.animate.start();
}

// 处理动画点位所需要的json
const aniPointJsonData = () => {
  const features = props.warningList.map((coordinates) => ({
    type:"Feature",
    geometry:{ type:"Point",coordinates },
  })) 
  return {
    data:{ type:"FeatureCollection", features }
  }
}

// 渲染预警点位
const renderAniPoint = () => {
  const { isShowPointWarning } = props;

  if(!isShowPointWarning) return;

  const geoLevelF = new Loca.GeoJSONSource(aniPointJsonData());
  const breathRed = new Loca.ScatterLayer({
    loca:locaInstance.value,
    zIndex:113,
    opacity:1,
    visible:true,
    zooms:[2,22]
  })

  breathRed.setSource(geoLevelF);
  breathRed.setStyle({
    unit:"meter",
    size:[8000,8000],
    texture:breathRedPng,
    duration:500,
    animate:true,
  })

  // 启动动画
  locaInstance.value.animate.start();
}

// 初始化地图
const initMapInstance = (AMap:any) => {
  const options:{
    [key:string]: any
  } = {
    mapStyle:props.mapStyle,
    doubleClickZoom:props.doubleClickZoom,
    center:props.center,
    zoom:props.zoom,
    zooms:props.zooms,
    // 图层集合，卫星地图、卫星路网都属于图层，push到这个layers就可以了
    layers:[],
    // 俯视角度
    pitch:props.pitch
  };

  // 判断地图是3D显示还是2D
  if(props.isShow3D){
    options.viewMode = "3D";
  }

  const district = new AMap.DistrictSearch({
    subdistrict:props.subdistrict,
    extensions:props.extensions,
    level:props.level
  })

  district.search(props.areaName,function(status:string,result:any){
    const bounds = result.districtList[0]["boundaries"];

    // 获取区域各坐标
    const mask = [];
    for(let i = 0;i<bounds.length; i+=1){
      mask.push([bounds[i]])
    }

    // 不显示区域外的地图
    if(!props.polylineConfig.showOuter){
      options.mask = mask;
    }

    // 展示卫星地图
    if(props.isShowSatellite){
      options.layers.push(new AMap.TileLayer.Satellite())
    }

    // 展示卫星路网
    if(props.isShowRoadNet){
      options.layers.push(new AMap.TileLayer.RoadNet())
    }

    mapInstance.value = new AMap.Map("map",options);

    // 渲染边界
    renderPolyLine(bounds);

    // 渲染3D墙体
    render3dWall(bounds);

    // 渲染3D控制器
    renderControlBar();

    // 渲染线条
    renderLine();

    // 渲染点位
    renderPoint();

    // 渲染脉冲线
    renderLoca();

    // 渲染预警点位
    renderAniPoint();
  })
}

// 加载地图的方法
const loadAMap = () => {
  AMapLoader.load({
    key:props.apiConfig.key,
    version:props.apiConfig.version,
    plugins:props.apiConfig.plugins,
    Loca:{
      version:props.locaConfig.version
    }
  }).then((AMap) => {
    initMapInstance(AMap);
  })
}

onMounted(() => {
  loadAMap();
})
</script>

<template>
  <div id="map"></div>
</template>

<style scoped>
#map{
  width: 100vw;
  height: 100vh;
}
.amap-container{
  background-image: unset;
}
</style>